package com.cloud.sample.provider;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.auth.CredentialsProvider;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.model.DeleteObjectsRequest;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.cloud.common.context.AppContext;
import com.cloud.common.sentinel.Sentinel;
import com.cloud.common.util.JsonUtils;
import com.cloud.sample.provider.dto.AliyunOssCredentialDTO;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.Lists;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.io.InputStream;
import java.time.Duration;
import java.util.List;

@Sentinel
@Component
public class AliyunOssProvider {

    @Value("${oss.endpoint}")
    private String endpoint;

    @Value("${oss.accessKeyId}")
    private String accessKeyId;

    @Value("${oss.accessKeySecret}")
    private String accessKeySecret;

    @Value("${oss.roleArn}")
    private String roleArn;

    @Value("${oss.bucket}")
    private String bucket;

    @Value("${oss.region}")
    private String region;

    @Value("${oss.url}")
    private String url;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private long durationSeconds = 1800;
    private OSSClient ossClient;

    @PostConstruct
    public void init() {
        CredentialsProvider credentialsProvider = new DefaultCredentialProvider(accessKeyId, accessKeySecret);
        ossClient = new OSSClient(endpoint, credentialsProvider, null);
    }

    public void putObject(String fileName, InputStream input) {
        ossClient.putObject(bucket, fileName, input);
    }

    public void deleteObject(String fileName) {
        ossClient.deleteObject(bucket, fileName);
    }

    public void deleteObjects(List<String> fileNameList) {
        if (CollectionUtils.isEmpty(fileNameList)) {
            return;
        }
        DeleteObjectsRequest request = new DeleteObjectsRequest(bucket).withKeys(fileNameList);
        ossClient.deleteObjects(request);
    }

    public AliyunOssCredentialDTO getCredential() throws Exception {
        final String key = AliyunOssCredentialDTO.class.getSimpleName();
        AliyunOssCredentialDTO ossCredentialDTO = (AliyunOssCredentialDTO) redisTemplate.opsForValue().get(key);
        if (ossCredentialDTO != null) {
            return ossCredentialDTO;
        }
        Statement statement = new Statement();
        statement.setAction(Lists.newArrayList("oss:PutObject"));
        statement.setResource(Lists.newArrayList("acs:oss:*:*:" + bucket + "/*"));
        Policy policy = new Policy();
        policy.setStatement(Lists.newArrayList(statement));

        IClientProfile profile = DefaultProfile.getProfile(region, accessKeyId, accessKeySecret);
        DefaultAcsClient client = new DefaultAcsClient(profile);
        AssumeRoleRequest request = new AssumeRoleRequest();
        request.setMethod(MethodType.POST);
        request.setRoleArn(roleArn);
        request.setRoleSessionName(AppContext.getAppName());
        request.setPolicy(JsonUtils.toJSONString(policy));
        request.setDurationSeconds(durationSeconds);
        AssumeRoleResponse response = client.getAcsResponse(request);
        AssumeRoleResponse.Credentials credentials = response.getCredentials();

        AliyunOssCredentialDTO.CredentialsDTO credentialsDTO = new AliyunOssCredentialDTO.CredentialsDTO();
        credentialsDTO.setAccessKeyId(credentials.getAccessKeyId());
        credentialsDTO.setAccessKeySecret(credentials.getAccessKeySecret());
        credentialsDTO.setSecurityToken(credentials.getSecurityToken());
        credentialsDTO.setDurationSeconds(durationSeconds);

        ossCredentialDTO = new AliyunOssCredentialDTO();
        ossCredentialDTO.setCredentials(credentialsDTO);
        ossCredentialDTO.setEndpoint(endpoint);
        ossCredentialDTO.setBucket(bucket);
        ossCredentialDTO.setRegion(region);
        ossCredentialDTO.setUrl(url);

        redisTemplate.opsForValue().set(key, ossCredentialDTO, Duration.ofSeconds(durationSeconds - 300));
        return ossCredentialDTO;
    }

    @Data
    private static class Policy {
        @JsonProperty("Version")
        private String version = "1";

        @JsonProperty("Statement")
        private List<Statement> statement;
    }

    @Data
    private static class Statement {
        @JsonProperty("Action")
        private List<String> action;

        @JsonProperty("Resource")
        private List<String> resource;

        @JsonProperty("Effect")
        private String effect = "Allow";
    }
}
